package fun.yuxi.gateway.config;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import fun.yuxi.gateway.properties.SwaggerProperties;
import fun.yuxi.gateway.router.NacosRouteDefinitionRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.gateway.config.GatewayProperties;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.support.NameUtils;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;
import springfox.documentation.swagger.web.SwaggerResource;
import springfox.documentation.swagger.web.SwaggerResourcesProvider;

import java.util.*;
import java.util.stream.Collectors;

/**
 * Swagger聚合
 *
 * @author 剧终
 * @version V1.0
 * @date 2022年02月17日 11:05
 */
@Slf4j
@Primary
@Component
@RequiredArgsConstructor
@ConditionalOnProperty(name = "spring.yuxi.swagger.enable", havingValue = "true", matchIfMissing = true)
public class SwaggerConfig implements SwaggerResourcesProvider {

    private final RouteLocator routeLocator;
    private final GatewayProperties gatewayProperties;
    private final SwaggerProperties swaggerProperties;
    private final NacosRouteDefinitionRepository nacosRouteDefinitionRepository;
    /**
     * Swagger2默认的url后缀
     */
    public static final String SWAGGER2_URL = "/v3/api-docs?group={}";

    @Override
    public List<SwaggerResource> get() {
        Set<String> routeList = new HashSet<>();
        // 获取网关中配置的route
        routeLocator.getRoutes().flatMap(x -> Flux.just(x.getId())).subscribe(routeList::add);
        Set<RouteDefinition> routeDefinitionList = gatewayProperties.getRoutes().parallelStream()
                .filter(routeDefinition -> routeList.contains(routeDefinition.getId()))
                .collect(Collectors.toSet());

        // 同时从Nacos动态路由中查找
        nacosRouteDefinitionRepository.getRouteDefinitions().collectList().subscribe(routeDefinitionList::addAll);
        if (CollUtil.isEmpty(routeDefinitionList)) {
            return Collections.emptyList();
        }
        Map<RouteDefinition, List<PredicateDefinition>> routeDefinitionListMap = routeDefinitionList.parallelStream()
                .filter(routeDefinition -> routeList.contains(routeDefinition.getId()))
                .collect(Collectors.toMap(x -> x, RouteDefinition::getPredicates));

        return routeDefinitionListMap.entrySet().parallelStream().map(x -> {
            RouteDefinition routeDefinition = x.getKey();
            List<PredicateDefinition> definitionList = x.getValue();
            return definitionList.parallelStream().filter(y -> "Path".equalsIgnoreCase(y.getName()))
                    .map(y -> swaggerResource(routeDefinition.getId(), y))
                    .collect(Collectors.toList());
        }).flatMap(List::stream).collect(Collectors.toList());
    }

    private SwaggerResource swaggerResource(String name, PredicateDefinition predicateDefinition) {
        Map<String, String> definitionArgs = predicateDefinition.getArgs();
        String targetPattern = StrUtil.format(SWAGGER2_URL, swaggerProperties.getDefaultGroupName());
        String location = definitionArgs.get(NameUtils.GENERATED_NAME_PREFIX + "0").replace("/**", targetPattern);
        SwaggerResource swaggerResource = new SwaggerResource();
        swaggerResource.setName(name);
        swaggerResource.setLocation(location);
        swaggerResource.setSwaggerVersion("3.0.3");
        return swaggerResource;
    }

}